import os
import sys
import json
import shutil
import subprocess
import tempfile
import zipfile
from urllib.parse import urljoin
from datetime import datetime
import logging
import time

# 配置
REPO_URL = "https://gitee.com/your_username/your_repo"  # 替换为你的Gitee仓库地址
VERSION_FILE = "version.json"  # 版本信息文件
UPDATE_INTERVAL = 3600  # 检查更新间隔(秒)

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s - %(levelname)s - %(message)s",
    handlers=[logging.FileHandler("update.log"), logging.StreamHandler()]
)
logger = logging.getLogger(__name__)


def get_current_version():
    """获取当前版本信息"""
    try:
        if os.path.exists(VERSION_FILE):
            with open(VERSION_FILE, "r", encoding="utf-8") as f:
                return json.load(f)
    except Exception as e:
        logger.warning(f"读取本地版本信息失败: {e}")
    return {"version": "0.0.0", "commit": ""}


def get_latest_version():
    """从Gitee获取最新版本信息"""
    try:
        import requests
        # 尝试从raw.gitee.com获取版本文件
        raw_url = urljoin(REPO_URL.replace(
            "gitee.com", "raw.gitee.com") + "/", VERSION_FILE)
        logger.info(f"正在检查最新版本: {raw_url}")

        headers = {"User-Agent": "Python Update Checker"}
        response = requests.get(raw_url, headers=headers, timeout=10)
        if response.status_code == 200:
            return json.loads(response.text)

        # 如果失败，尝试从码云Pages或其他方式获取
        logger.warning(f"获取版本文件失败，状态码: {response.status_code}")
        return None
    except Exception as e:
        logger.error(f"获取最新版本信息失败: {e}")
        return None


def compare_versions(version1, version2):
    """比较版本号"""
    def normalize(v):
        return [int(x) for x in v.split(".")]
    return normalize(version1) < normalize(version2)


def download_and_extract(url, target_dir):
    """下载并解压文件"""
    try:
        import requests
        headers = {"User-Agent": "Python Update Checker"}
        response = requests.get(url, headers=headers, stream=True, timeout=30)
        response.raise_for_status()

        with tempfile.NamedTemporaryFile(delete=False, suffix=".zip") as tmp_file:
            for chunk in response.iter_content(chunk_size=8192):
                tmp_file.write(chunk)
            tmp_file_path = tmp_file.name

        with zipfile.ZipFile(tmp_file_path, "r") as zip_ref:
            # 提取根目录名称
            root_dir = zip_ref.namelist()[0].split("/")[0]
            zip_ref.extractall(target_dir)

        os.remove(tmp_file_path)
        return os.path.join(target_dir, root_dir)
    except Exception as e:
        logger.error(f"下载或解压文件失败: {e}")
        return None


def update_files(source_dir, target_dir, exclude_files=None):
    """更新文件"""
    if exclude_files is None:
        exclude_files = [VERSION_FILE, "update.log",
                         "venv", ".git", "__pycache__"]

    try:
        for root, dirs, files in os.walk(source_dir):
            # 过滤掉不需要的目录
            dirs[:] = [d for d in dirs if d not in exclude_files]

            for file in files:
                if file in exclude_files:
                    continue

                src_path = os.path.join(root, file)
                rel_path = os.path.relpath(src_path, source_dir)
                dst_path = os.path.join(target_dir, rel_path)

                os.makedirs(os.path.dirname(dst_path), exist_ok=True)

                # 只更新变化的文件
                if not os.path.exists(dst_path) or os.path.getmtime(src_path) > os.path.getmtime(dst_path):
                    shutil.copy2(src_path, dst_path)
                    logger.info(f"更新文件: {rel_path}")

        # 删除本地有但远程没有的文件
        for root, dirs, files in os.walk(target_dir):
            dirs[:] = [d for d in dirs if d not in exclude_files]

            for file in files:
                if file in exclude_files:
                    continue

                dst_path = os.path.join(root, file)
                rel_path = os.path.relpath(dst_path, target_dir)
                src_path = os.path.join(source_dir, rel_path)

                if not os.path.exists(src_path):
                    os.remove(dst_path)
                    logger.info(f"删除文件: {rel_path}")

        return True
    except Exception as e:
        logger.error(f"更新文件失败: {e}")
        return False


def restart_program():
    """重启程序"""
    logger.info("更新完成，正在重启程序...")
    python = sys.executable
    script = sys.argv[0]
    args = sys.argv[1:]
    subprocess.Popen([python, script] + args)
    sys.exit(0)


def check_for_updates():
    """检查并执行更新"""
    current_version = get_current_version()
    logger.info(f"当前版本: {current_version['version']}")

    latest_version = get_latest_version()
    if not latest_version:
        logger.warning("无法获取最新版本信息，跳过更新检查")
        return False

    logger.info(f"最新版本: {latest_version['version']}")

    if compare_versions(current_version["version"], latest_version["version"]):
        logger.info("发现新版本，准备更新...")

        # 下载最新代码
        # 假设使用最新的zip包
        download_url = f"{REPO_URL}/archive/master.zip"
        temp_dir = tempfile.mkdtemp()
        extracted_dir = download_and_extract(download_url, temp_dir)

        if not extracted_dir:
            shutil.rmtree(temp_dir)
            return False

        # 更新文件
        if update_files(extracted_dir, os.getcwd()):
            # 更新版本信息
            with open(VERSION_FILE, "w", encoding="utf-8") as f:
                json.dump(latest_version, f, ensure_ascii=False, indent=4)

            shutil.rmtree(temp_dir)
            logger.info("更新成功")
            return True

        shutil.rmtree(temp_dir)
        return False

    logger.info("当前已是最新版本")
    return False


def run_with_auto_update(main_func):
    """带自动更新的主程序运行器"""
    last_check_time = 0

    while True:
        # 检查是否需要更新
        current_time = time.time()
        if current_time - last_check_time >= UPDATE_INTERVAL:
            if check_for_updates():
                restart_program()
            last_check_time = current_time

        # 运行主程序
        try:
            main_func()
        except Exception as e:
            logger.error(f"主程序出错: {e}")
            time.sleep(5)  # 等待5秒后重试


# 使用示例
if __name__ == "__main__":
    def main():
        """你的主程序逻辑"""
        logger.info("主程序正在运行...")
        time.sleep(10)  # 模拟主程序运行

    run_with_auto_update(main)
